package org.jeecg.modules.meta.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import org.apache.commons.lang.StringUtils;
import org.jeecg.metasyn.entity.FdColumn;
import org.jeecg.metasyn.entity.FdTable;
import org.jeecg.metasyn.mapper.FdColumnMapper;
import org.jeecg.metasyn.mapper.FdTableMapper;
import org.jeecg.modules.meta.entity.SysFieldinfo;
import org.jeecg.modules.meta.entity.SysTableinfo;
import org.jeecg.modules.meta.mapper.SysFieldinfoMapper;
import org.jeecg.modules.meta.mapper.SysTableinfoMapper;
import org.jeecg.modules.meta.service.ISysFieldinfoService;
import org.jeecg.modules.meta.service.ISysTableinfoService;
import org.jeecg.modules.meta.service.MetaSynDBOperService;
import org.jeecg.modules.meta.service.MetaSynService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

/**
 * 元数据同步抽象类
 * 支持全量同步和按天同步
 */
public abstract class AbstractMetaSynDBOperService implements MetaSynDBOperService {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    private String desc = "元数据同步";

    @Autowired
    MetaSynService metaSynService;

    @Autowired
    FdTableMapper fdTableMapper;

    @Autowired
    FdColumnMapper fdColumnMapper;

    @Autowired
    SysTableinfoMapper sysTableinfoMapper;

    @Autowired
    ISysTableinfoService iSysTableinfoService;

    @Autowired
    SysFieldinfoMapper sysFieldinfoMapper;

    @Autowired
    ISysFieldinfoService iSysFieldinfoService;

    /**
     * 获取需要同步得表数据
     */
    abstract List<FdTable> synMetaTablePage(long current, long size, Date date);

    /**
     * 获取需要同步得字段数据
     */
    abstract List<FdColumn> synMetaFieldPage(long current, long size, Date date);

    /**
     * 输出日志
     */
    abstract void printLog(Date date);

    /**
     * 删除一分钟之前得本次没有更新的数据，逻辑删除状态变成2
     */
    abstract void updateStatusByTime(Date date);

    /**
     * 删除一分钟之前得本次没有更新的数据，逻辑删除状态变成2
     */
    abstract void updateFieldStatusByTime(Date date);


    protected boolean synMetaTable(Date date) {
        Date delDate = new Date(System.currentTimeMillis() - 1000 * 60);

        //数据上限-50万张表
        for (int i = 1; i < 500; i++) {
            List<FdTable> fdTables = synMetaTablePage(i, 1000, date);
            if (null == fdTables || fdTables.size() == 0) {
                break;
            }

            //解析表新增，修改，删除
            analysisTable(fdTables);
        }

        //删除操作
        updateStatusByTime(delDate);
        return true;
    }

    boolean synMetaField(Date date) {
        Date delDate = new Date(System.currentTimeMillis() - 1000 * 60);

        //数据上限-500万字段个数
        for (int i = 1; i < 5000; i++) {
            List<FdColumn> columns = synMetaFieldPage(i, 1000, date);
            if (null == columns || columns.size() == 0) {
                break;
            }

            analysisField(columns);
        }
        //删除操作
        updateFieldStatusByTime(delDate);
        return true;
    }

    /**
     * 同步元数据数据
     */
    private boolean synMetaDB(Date date) {
        try {
            //同步表
            if (!synMetaTable(date)) {
                return false;
            }
            log.info("执行元数据表同步结束：");

            //同步字段
            if (!synMetaField(date)) {
                return false;
            }
        } catch (Exception e) {
            log.error("执行元数据同步错误：", e);
            return false;
        }
        return true;
    }

    @Override
    public boolean synMeta(Date date) {
        printLog(date);
        Long time = System.currentTimeMillis();
        //数据同步判断，如果正在同步 返回false
        if (isSyn()) {
            return false;
        }
        //设置正在同步标识
        startSyn();

        log.info("元数据同步:正在执行ing");
        //同步元数据
        boolean blean = synMetaDB(date);
        log.info("元数据同步结束:耗时" + (System.currentTimeMillis() - time) / 1000);

        //同步结束
        endSyn();
        return blean;
    }

    protected void analysisTable(List<FdTable> tableList) {
        List updateAay = new LinkedList();
        List insertAay = new LinkedList();

        SysTableinfo sysTableinfo;

        for (FdTable fdTable : tableList) {
            sysTableinfo = selectOne(fdTable.getFdDataid());
            if (null != sysTableinfo) {//更新
                setTablePubField(sysTableinfo, fdTable);
                updateAay.add(sysTableinfo);
            } else {//插入
                sysTableinfo = new SysTableinfo();
                setTablePubField(sysTableinfo, fdTable);
                sysTableinfo.setFdTablename(fdTable.getFdTablename());
                sysTableinfo.setFdTableid(fdTable.getFdDataid());
                sysTableinfo.setFdSysid("");//系统id从哪里取？
                sysTableinfo.setCreateTime(new Date());
                insertAay.add(sysTableinfo);
            }
        }

        runBatCh(iSysTableinfoService, updateAay, insertAay);
    }

    protected void runBatCh(IService iService, List updateAay, List insertAay) {
        int runNum = 100;
        if (updateAay != null && updateAay.size() != 0) {
            iService.updateBatchById(updateAay, runNum);
        }

        if (insertAay != null && insertAay.size() != 0) {
            iService.saveBatch(insertAay, runNum);
        }
    }

    private String getFdTabIdForMap(FdColumn fdColumn, Map<String, String> tabIdMap) {
        if (tabIdMap.containsKey(fdColumn.getFdTablepath())) {
            return tabIdMap.get(fdColumn.getFdTablepath());
        }
        String tabId = fdColumn.getFdDataid();

        QueryWrapper<FdTable> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fd_tablepath", fdColumn.getFdTablepath());
        FdTable fdTable = fdTableMapper.selectOne(queryWrapper);

        if (fdTable != null || StringUtils.isNotEmpty(fdTable.getFdDataid())) {
            tabId = fdTable.getFdDataid();
        }

        tabIdMap.put(fdColumn.getFdTablepath(), tabId);
        return tabId;
    }

    protected void analysisField(List<FdColumn> columns) {
        List updateAay = new LinkedList();
        List insertAay = new LinkedList();
        Map<String, String> tabIdMap = new HashMap<>();

        for (FdColumn fdColumn : columns) {
            fdColumn.setFdDataid(getFdTabIdForMap(fdColumn, tabIdMap));

            SysFieldinfo sysFieldinfo = getSysFieldinfo(fdColumn);
            if (null != sysFieldinfo) {//更新
                setFieldInfoPubField(sysFieldinfo, fdColumn);
                updateAay.add(sysFieldinfo);
            } else {//插入
                sysFieldinfo = new SysFieldinfo();
                setFieldInfoPubField(sysFieldinfo, fdColumn);
                sysFieldinfo.setFdTableid(fdColumn.getFdDataid() + "");
                sysFieldinfo.setFdFieldname(fdColumn.getFdFieldname());

                sysFieldinfo.setFdFieldid(UUID.randomUUID().toString());
                sysFieldinfo.setCreateTime(new Date());
                insertAay.add(sysFieldinfo);
            }
        }

        runBatCh(iSysFieldinfoService, updateAay, insertAay);
    }

    private SysFieldinfo getSysFieldinfo(FdColumn fdColumn) {
        QueryWrapper<SysFieldinfo> wrapper = new QueryWrapper<>();
        wrapper.eq("fd_tableid", fdColumn.getFdDataid());
        wrapper.eq("fd_fieldname", fdColumn.getFdFieldname());
        SysFieldinfo sysFieldinfo = sysFieldinfoMapper.selectOne(wrapper);
        if (null == sysFieldinfo) {
            return null;
        }
        return sysFieldinfo;
    }

    private SysTableinfo selectOne(String fdDataID) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fd_tableid", fdDataID);
        return sysTableinfoMapper.selectOne(queryWrapper);
    }

    private void setFieldInfoPubField(SysFieldinfo sysFieldinfo, FdColumn fdColumn) {
        sysFieldinfo.setFdFielddesc(fdColumn.getFdFielddesc());
        sysFieldinfo.setFdFieldlength(fdColumn.getFdFieldlength());
        sysFieldinfo.setFdFieldorder(fdColumn.getFdFieldorder());
        sysFieldinfo.setFdFieldscale(fdColumn.getFdFieldscale());
        sysFieldinfo.setFdFieldtype(fdColumn.getFdFieldtype());
        sysFieldinfo.setFdRulelevel(fdColumn.getFdRulelevel());
        sysFieldinfo.setFdRulename(fdColumn.getFdRuletype());
        sysFieldinfo.setFdStatus("0");
        sysFieldinfo.setUpdateTime(new Date());
    }

    private void setTablePubField(SysTableinfo sysTableinfo, FdTable fdTable) {
        sysTableinfo.setFdSchema(fdTable.getFdSchema());
        sysTableinfo.setFdTabledesc(fdTable.getFdTabledesc());
        sysTableinfo.setFdLoaddt(fdTable.getFdLoaddt());
        sysTableinfo.setFdCreatetime(fdTable.getFdCreatetime());
        sysTableinfo.setFdUpdatetime(fdTable.getFdUpdatetime());
        sysTableinfo.setFdStatus("0");
        sysTableinfo.setUpdateTime(new Date());
    }


    private synchronized boolean isSyn() {
        return metaSynService.isSyn();
    }

    /**
     * 设置正在同步
     */
    private void startSyn() {
        metaSynService.startSyn(desc);
    }

    /**
     * 设置同步结束
     */
    private void endSyn() {
        metaSynService.endSyn(desc);
    }
}
